package com.zhentao.ai.algorithm;

import com.zhentao.ai.model.FeatureVector;
import com.zhentao.ai.model.ParamUsagePattern;
import com.zhentao.ai.model.PerformanceMetrics;
import com.zhentao.ai.model.UsageRecord;
import com.zhentao.ai.model.features.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 特征工程算法
 * 从原始数据中提取和构造有用的特征用于机器学习
 * 
 * @author zhentao
 * @date 2024-12-19
 */
@Slf4j
@Component
public class FeatureEngineering {
    
    /**
     * 从参数使用模式中提取特征
     */
    public FeatureVector extractFeatures(ParamUsagePattern pattern) {
        if (pattern == null || pattern.getUsageHistory().isEmpty()) {
            throw new IllegalArgumentException("参数使用模式数据不能为空");
        }
        
        log.info("开始特征提取，参数: {}, 数据点: {}", pattern.getParamKey(), pattern.getUsageHistory().size());
        
        FeatureVector features = new FeatureVector();
        features.setParamKey(pattern.getParamKey());
        features.setExtractionTime(LocalDateTime.now());
        
        // 1. 时间特征
        features.setTimeFeatures(extractTimeFeatures(pattern.getUsageHistory()));
        
        // 2. 统计特征
        features.setStatisticalFeatures(extractStatisticalFeatures(pattern.getUsageHistory()));
        
        // 3. 趋势特征
        features.setTrendFeatures(extractTrendFeatures(pattern.getUsageHistory()));
        
        // 4. 周期性特征
        features.setCyclicalFeatures(extractCyclicalFeatures(pattern.getUsageHistory()));
        
        // 5. 性能特征
        if (pattern.getPerformanceMetrics() != null) {
            features.setPerformanceFeatures(extractPerformanceFeatures(pattern.getPerformanceMetrics()));
        }
        
        // 6. 异常特征
        features.setAnomalyFeatures(extractAnomalyFeatures(pattern.getUsageHistory()));
        
        log.info("特征提取完成，共提取{}个特征维度", calculateTotalFeatures(features));
        return features;
    }
    
    /**
     * 提取时间特征
     */
    private TimeFeatures extractTimeFeatures(List<UsageRecord> usageHistory) {
        TimeFeatures timeFeatures = new TimeFeatures();
        
        if (usageHistory.isEmpty()) return timeFeatures;
        
        // 基础时间统计
        LocalDateTime startTime = usageHistory.get(0).getTimestamp();
        LocalDateTime endTime = usageHistory.get(usageHistory.size() - 1).getTimestamp();
        timeFeatures.setTimeSpanHours(ChronoUnit.HOURS.between(startTime, endTime));
        timeFeatures.setDataPointsCount(usageHistory.size());
        
        // 时间间隔分析
        List<Long> intervals = new ArrayList<>();
        for (int i = 1; i < usageHistory.size(); i++) {
            long interval = ChronoUnit.MINUTES.between(
                usageHistory.get(i-1).getTimestamp(), 
                usageHistory.get(i).getTimestamp()
            );
            intervals.add(interval);
        }
        
        if (!intervals.isEmpty()) {
            timeFeatures.setAvgIntervalMinutes(intervals.stream().mapToLong(Long::longValue).average().orElse(0));
            timeFeatures.setMaxIntervalMinutes(intervals.stream().mapToLong(Long::longValue).max().orElse(0));
            timeFeatures.setMinIntervalMinutes(intervals.stream().mapToLong(Long::longValue).min().orElse(0));
        }
        
        // 活跃时段分析
        Map<Integer, Long> hourlyDistribution = usageHistory.stream()
                .collect(Collectors.groupingBy(
                    record -> record.getTimestamp().getHour(),
                    Collectors.counting()
                ));
        
        timeFeatures.setPeakHour(hourlyDistribution.entrySet().stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey)
                .orElse(0));
        
        // 工作日vs周末分析
        long weekdayCount = usageHistory.stream()
                .filter(record -> isWeekday(record.getTimestamp().getDayOfWeek()))
                .count();
        timeFeatures.setWeekdayRatio((double) weekdayCount / usageHistory.size());
        
        return timeFeatures;
    }
    
    /**
     * 提取统计特征
     */
    private StatisticalFeatures extractStatisticalFeatures(List<UsageRecord> usageHistory) {
        StatisticalFeatures statFeatures = new StatisticalFeatures();
        
        List<Double> accessCounts = usageHistory.stream()
                .map(record -> record.getAccessCount().doubleValue())
                .collect(Collectors.toList());
        
        if (accessCounts.isEmpty()) return statFeatures;
        
        // 基础统计量
        double sum = accessCounts.stream().mapToDouble(Double::doubleValue).sum();
        double mean = sum / accessCounts.size();
        double variance = accessCounts.stream()
                .mapToDouble(value -> Math.pow(value - mean, 2))
                .sum() / accessCounts.size();
        double stdDev = Math.sqrt(variance);
        
        statFeatures.setMean(BigDecimal.valueOf(mean).setScale(3, RoundingMode.HALF_UP));
        statFeatures.setStandardDeviation(BigDecimal.valueOf(stdDev).setScale(3, RoundingMode.HALF_UP));
        statFeatures.setVariance(BigDecimal.valueOf(variance).setScale(3, RoundingMode.HALF_UP));
        
        // 分位数
        List<Double> sortedValues = accessCounts.stream().sorted().collect(Collectors.toList());
        statFeatures.setMedian(BigDecimal.valueOf(getPercentile(sortedValues, 50)).setScale(3, RoundingMode.HALF_UP));
        statFeatures.setQ1(BigDecimal.valueOf(getPercentile(sortedValues, 25)).setScale(3, RoundingMode.HALF_UP));
        statFeatures.setQ3(BigDecimal.valueOf(getPercentile(sortedValues, 75)).setScale(3, RoundingMode.HALF_UP));
        statFeatures.setIqr(statFeatures.getQ3().subtract(statFeatures.getQ1()));
        
        // 极值
        statFeatures.setMin(BigDecimal.valueOf(Collections.min(accessCounts)));
        statFeatures.setMax(BigDecimal.valueOf(Collections.max(accessCounts)));
        statFeatures.setRange(statFeatures.getMax().subtract(statFeatures.getMin()));
        
        // 形状特征
        statFeatures.setSkewness(BigDecimal.valueOf(calculateSkewness(accessCounts, mean, stdDev)));
        statFeatures.setKurtosis(BigDecimal.valueOf(calculateKurtosis(accessCounts, mean, stdDev)));
        
        // 变异系数
        if (mean != 0) {
            statFeatures.setCoefficientOfVariation(statFeatures.getStandardDeviation().divide(statFeatures.getMean(), 3, RoundingMode.HALF_UP));
        }
        
        return statFeatures;
    }
    
    /**
     * 提取趋势特征
     */
    private TrendFeatures extractTrendFeatures(List<UsageRecord> usageHistory) {
        TrendFeatures trendFeatures = new TrendFeatures();
        
        if (usageHistory.size() < 3) return trendFeatures;
        
        List<Double> values = usageHistory.stream()
                .map(record -> record.getAccessCount().doubleValue())
                .collect(Collectors.toList());
        
        // 线性趋势 (简单线性回归斜率)
        double slope = calculateLinearTrendSlope(values);
        trendFeatures.setLinearTrend(BigDecimal.valueOf(slope).setScale(6, RoundingMode.HALF_UP));
        
        // 趋势方向
        if (slope > 0.001) {
            trendFeatures.setTrendDirection("INCREASING");
        } else if (slope < -0.001) {
            trendFeatures.setTrendDirection("DECREASING");
        } else {
            trendFeatures.setTrendDirection("STABLE");
        }
        
        // 趋势强度 (R²)
        double rSquared = calculateTrendStrength(values, slope);
        trendFeatures.setTrendStrength(BigDecimal.valueOf(rSquared).setScale(3, RoundingMode.HALF_UP));
        
        // 单调性检验
        trendFeatures.setMonotonicity(calculateMonotonicity(values));
        
        // 变化率分析
        List<Double> changeRates = new ArrayList<>();
        for (int i = 1; i < values.size(); i++) {
            if (values.get(i-1) != 0) {
                double changeRate = (values.get(i) - values.get(i-1)) / values.get(i-1);
                changeRates.add(changeRate);
            }
        }
        
        if (!changeRates.isEmpty()) {
            double avgChangeRate = changeRates.stream().mapToDouble(Double::doubleValue).average().orElse(0);
            trendFeatures.setAverageChangeRate(BigDecimal.valueOf(avgChangeRate).setScale(6, RoundingMode.HALF_UP));
        }
        
        return trendFeatures;
    }
    
    /**
     * 提取周期性特征
     */
    private CyclicalFeatures extractCyclicalFeatures(List<UsageRecord> usageHistory) {
        CyclicalFeatures cyclicalFeatures = new CyclicalFeatures();
        
        if (usageHistory.size() < 24) return cyclicalFeatures; // 至少需要24个数据点
        
        // 小时级周期性
        Map<Integer, Double> hourlyPattern = analyzeHourlyPattern(usageHistory);
        cyclicalFeatures.setHourlyPattern(hourlyPattern);
        cyclicalFeatures.setHourlyVariation(calculatePatternVariation(hourlyPattern));
        
        // 星期级周期性
        Map<Integer, Double> weeklyPattern = analyzeWeeklyPattern(usageHistory);
        cyclicalFeatures.setWeeklyPattern(weeklyPattern);
        cyclicalFeatures.setWeeklyVariation(calculatePatternVariation(weeklyPattern));
        
        // 周期性强度
        cyclicalFeatures.setPeriodicity(calculatePeriodicity(usageHistory));
        
        return cyclicalFeatures;
    }
    
    /**
     * 提取性能特征
     */
    private PerformanceBasedFeatures extractPerformanceFeatures(PerformanceMetrics metrics) {
        PerformanceBasedFeatures perfFeatures = new PerformanceBasedFeatures();
        
        perfFeatures.setAverageResponseTime(metrics.getAverageResponseTime());
        perfFeatures.setP95ResponseTime(metrics.getP95ResponseTime());
        perfFeatures.setThroughput(metrics.getThroughput());
        perfFeatures.setErrorRate(metrics.getErrorRate());
        perfFeatures.setCacheHitRate(metrics.getCacheHitRate());
        
        // 性能稳定性指标
        if (metrics.getResponseTimes() != null && !metrics.getResponseTimes().isEmpty()) {
            double mean = metrics.getResponseTimes().stream().mapToDouble(Double::doubleValue).average().orElse(0);
            double variance = metrics.getResponseTimes().stream()
                    .mapToDouble(rt -> Math.pow(rt - mean, 2))
                    .average().orElse(0);
            perfFeatures.setResponseTimeStability(BigDecimal.valueOf(1.0 / (1.0 + variance)).setScale(3, RoundingMode.HALF_UP));
        }
        
        // 性能评分 (综合指标)
        double performanceScore = calculatePerformanceScore(metrics);
        perfFeatures.setOverallPerformanceScore(BigDecimal.valueOf(performanceScore).setScale(3, RoundingMode.HALF_UP));
        
        return perfFeatures;
    }
    
    /**
     * 提取异常特征
     */
    private AnomalyBasedFeatures extractAnomalyFeatures(List<UsageRecord> usageHistory) {
        AnomalyBasedFeatures anomalyFeatures = new AnomalyBasedFeatures();
        
        List<Double> values = usageHistory.stream()
                .map(record -> record.getAccessCount().doubleValue())
                .collect(Collectors.toList());
        
        if (values.isEmpty()) return anomalyFeatures;
        
        // 异常值数量 (基于3-sigma规则)
        double mean = values.stream().mapToDouble(Double::doubleValue).average().orElse(0);
        double stdDev = Math.sqrt(values.stream().mapToDouble(v -> Math.pow(v - mean, 2)).average().orElse(0));
        
        long outlierCount = values.stream()
                .mapToLong(value -> Math.abs(value - mean) > 3 * stdDev ? 1 : 0)
                .sum();
        
        anomalyFeatures.setOutlierCount((int) outlierCount);
        anomalyFeatures.setOutlierRatio(BigDecimal.valueOf((double) outlierCount / values.size()).setScale(3, RoundingMode.HALF_UP));
        
        // 突变点检测
        int jumpPoints = detectJumpPoints(values);
        anomalyFeatures.setJumpPointCount(jumpPoints);
        
        // 异常聚集度
        anomalyFeatures.setAnomalyClusterDensity(calculateAnomalyClusterDensity(values, mean, stdDev));
        
        return anomalyFeatures;
    }
    
    // 辅助计算方法
    private boolean isWeekday(DayOfWeek dayOfWeek) {
        return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
    }
    
    private double getPercentile(List<Double> sortedValues, int percentile) {
        if (sortedValues.isEmpty()) return 0;
        int index = (int) Math.ceil(percentile / 100.0 * sortedValues.size()) - 1;
        index = Math.max(0, Math.min(index, sortedValues.size() - 1));
        return sortedValues.get(index);
    }
    
    private double calculateSkewness(List<Double> values, double mean, double stdDev) {
        if (stdDev == 0) return 0;
        return values.stream()
                .mapToDouble(value -> Math.pow((value - mean) / stdDev, 3))
                .average()
                .orElse(0);
    }
    
    private double calculateKurtosis(List<Double> values, double mean, double stdDev) {
        if (stdDev == 0) return 0;
        return values.stream()
                .mapToDouble(value -> Math.pow((value - mean) / stdDev, 4))
                .average()
                .orElse(0) - 3; // 减去3得到超峰度
    }
    
    private double calculateLinearTrendSlope(List<Double> values) {
        int n = values.size();
        double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
        
        for (int i = 0; i < n; i++) {
            sumX += i;
            sumY += values.get(i);
            sumXY += i * values.get(i);
            sumX2 += i * i;
        }
        
        double denominator = n * sumX2 - sumX * sumX;
        if (denominator == 0) return 0;
        
        return (n * sumXY - sumX * sumY) / denominator;
    }
    
    private double calculateTrendStrength(List<Double> values, double slope) {
        // 计算R²
        double mean = values.stream().mapToDouble(Double::doubleValue).average().orElse(0);
        double totalSumSquares = values.stream()
                .mapToDouble(value -> Math.pow(value - mean, 2))
                .sum();
        
        if (totalSumSquares == 0) return 0;
        
        double residualSumSquares = 0;
        for (int i = 0; i < values.size(); i++) {
            double predicted = slope * i + (mean - slope * (values.size() - 1) / 2.0);
            residualSumSquares += Math.pow(values.get(i) - predicted, 2);
        }
        
        return 1 - (residualSumSquares / totalSumSquares);
    }
    
    private BigDecimal calculateMonotonicity(List<Double> values) {
        int increasing = 0, decreasing = 0;
        for (int i = 1; i < values.size(); i++) {
            if (values.get(i) > values.get(i-1)) increasing++;
            else if (values.get(i) < values.get(i-1)) decreasing++;
        }
        
        int total = values.size() - 1;
        if (total == 0) return BigDecimal.ZERO;
        
        return BigDecimal.valueOf(Math.max(increasing, decreasing) / (double) total).setScale(3, RoundingMode.HALF_UP);
    }
    
    private Map<Integer, Double> analyzeHourlyPattern(List<UsageRecord> usageHistory) {
        Map<Integer, List<Double>> hourlyGroups = usageHistory.stream()
                .collect(Collectors.groupingBy(
                    record -> record.getTimestamp().getHour(),
                    Collectors.mapping(record -> record.getAccessCount().doubleValue(), Collectors.toList())
                ));
        
        Map<Integer, Double> pattern = new HashMap<>();
        for (Map.Entry<Integer, List<Double>> entry : hourlyGroups.entrySet()) {
            double avg = entry.getValue().stream().mapToDouble(Double::doubleValue).average().orElse(0);
            pattern.put(entry.getKey(), avg);
        }
        
        return pattern;
    }
    
    private Map<Integer, Double> analyzeWeeklyPattern(List<UsageRecord> usageHistory) {
        Map<Integer, List<Double>> weeklyGroups = usageHistory.stream()
                .collect(Collectors.groupingBy(
                    record -> record.getTimestamp().getDayOfWeek().getValue(),
                    Collectors.mapping(record -> record.getAccessCount().doubleValue(), Collectors.toList())
                ));
        
        Map<Integer, Double> pattern = new HashMap<>();
        for (Map.Entry<Integer, List<Double>> entry : weeklyGroups.entrySet()) {
            double avg = entry.getValue().stream().mapToDouble(Double::doubleValue).average().orElse(0);
            pattern.put(entry.getKey(), avg);
        }
        
        return pattern;
    }
    
    private BigDecimal calculatePatternVariation(Map<Integer, Double> pattern) {
        if (pattern.isEmpty()) return BigDecimal.ZERO;
        
        double mean = pattern.values().stream().mapToDouble(Double::doubleValue).average().orElse(0);
        double variance = pattern.values().stream()
                .mapToDouble(value -> Math.pow(value - mean, 2))
                .average()
                .orElse(0);
        
        return BigDecimal.valueOf(Math.sqrt(variance) / (mean + 0.001)).setScale(3, RoundingMode.HALF_UP);
    }
    
    private BigDecimal calculatePeriodicity(List<UsageRecord> usageHistory) {
        // 简化的周期性计算 - 基于自相关
        List<Double> values = usageHistory.stream()
                .map(record -> record.getAccessCount().doubleValue())
                .collect(Collectors.toList());
        
        if (values.size() < 12) return BigDecimal.ZERO;
        
        double maxAutocorr = 0;
        for (int lag = 1; lag <= Math.min(values.size() / 3, 24); lag++) {
            double autocorr = calculateAutocorrelation(values, lag);
            maxAutocorr = Math.max(maxAutocorr, Math.abs(autocorr));
        }
        
        return BigDecimal.valueOf(maxAutocorr).setScale(3, RoundingMode.HALF_UP);
    }
    
    private double calculateAutocorrelation(List<Double> values, int lag) {
        double mean = values.stream().mapToDouble(Double::doubleValue).average().orElse(0);
        double numerator = 0, denominator = 0;
        
        for (int i = lag; i < values.size(); i++) {
            numerator += (values.get(i) - mean) * (values.get(i - lag) - mean);
        }
        
        for (double value : values) {
            denominator += Math.pow(value - mean, 2);
        }
        
        return denominator != 0 ? numerator / denominator : 0;
    }
    
    private double calculatePerformanceScore(PerformanceMetrics metrics) {
        double score = 1.0;
        
        // 响应时间权重40%
        if (metrics.getAverageResponseTime() != null) {
            double responseScore = Math.max(0, 1 - metrics.getAverageResponseTime().doubleValue() / 1000.0);
            score *= Math.pow(responseScore, 0.4);
        }
        
        // 错误率权重30%
        if (metrics.getErrorRate() != null) {
            double errorScore = Math.max(0, 1 - metrics.getErrorRate().doubleValue());
            score *= Math.pow(errorScore, 0.3);
        }
        
        // 缓存命中率权重30%
        if (metrics.getCacheHitRate() != null) {
            score *= Math.pow(metrics.getCacheHitRate().doubleValue(), 0.3);
        }
        
        return score;
    }
    
    private int detectJumpPoints(List<Double> values) {
        if (values.size() < 3) return 0;
        
        int jumpPoints = 0;
        double threshold = 2.0; // 跳跃阈值倍数
        
        for (int i = 1; i < values.size() - 1; i++) {
            double prev = values.get(i - 1);
            double curr = values.get(i);
            // 注：next变量预留给未来的跳跃模式识别算法使用
            // double next = values.get(i + 1);
            
            if (prev != 0 && Math.abs(curr - prev) / prev > threshold) {
                jumpPoints++;
            }
        }
        
        return jumpPoints;
    }
    
    private BigDecimal calculateAnomalyClusterDensity(List<Double> values, double mean, double stdDev) {
        // 计算异常值的聚集程度
        List<Integer> anomalyIndices = new ArrayList<>();
        for (int i = 0; i < values.size(); i++) {
            if (Math.abs(values.get(i) - mean) > 2 * stdDev) {
                anomalyIndices.add(i);
            }
        }
        
        if (anomalyIndices.size() < 2) return BigDecimal.ZERO;
        
        // 计算异常点之间的平均距离
        double totalDistance = 0;
        int pairCount = 0;
        for (int i = 0; i < anomalyIndices.size(); i++) {
            for (int j = i + 1; j < anomalyIndices.size(); j++) {
                totalDistance += Math.abs(anomalyIndices.get(i) - anomalyIndices.get(j));
                pairCount++;
            }
        }
        
        double avgDistance = totalDistance / pairCount;
        double density = 1.0 / (1.0 + avgDistance / values.size());
        
        return BigDecimal.valueOf(density).setScale(3, RoundingMode.HALF_UP);
    }
    
    private int calculateTotalFeatures(FeatureVector features) {
        int count = 0;
        if (features.getTimeFeatures() != null) count += 6; // 预估时间特征数量
        if (features.getStatisticalFeatures() != null) count += 12; // 预估统计特征数量
        if (features.getTrendFeatures() != null) count += 5; // 预估趋势特征数量
        if (features.getCyclicalFeatures() != null) count += 4; // 预估周期性特征数量
        if (features.getPerformanceFeatures() != null) count += 6; // 预估性能特征数量
        if (features.getAnomalyFeatures() != null) count += 4; // 预估异常特征数量
        return count;
    }
}
